package com.gitee.feizns.itrip.personal.biz.service.impl;

import com.gitee.feizns.itrip.personal.beans.pojo.HotelOrder;
import com.gitee.feizns.itrip.personal.beans.pojo.HotelTempStore;
import com.gitee.feizns.itrip.personal.beans.vo.Page;
import com.gitee.feizns.itrip.personal.beans.vo.impl.PageVoForMybatisPlus;
import com.gitee.feizns.itrip.personal.biz.entity.ao.ForAcceptAddHotelOrder;
import com.gitee.feizns.itrip.personal.biz.entity.ao.ForAcceptGetPersonalOrderList;
import com.gitee.feizns.itrip.personal.biz.entity.ao.ForAcceptPreOrderInfo;
import com.gitee.feizns.itrip.personal.biz.entity.vo.ForViewAddHotelOrder;
import com.gitee.feizns.itrip.personal.biz.entity.vo.ForViewGetPersonalOrderInfo;
import com.gitee.feizns.itrip.personal.biz.entity.vo.ForViewGetPersonalOrderRoomInfo;
import com.gitee.feizns.itrip.personal.biz.entity.vo.ForViewPersonalOrderList;
import com.gitee.feizns.itrip.personal.biz.entity.vo.ForViewPreOrderInfo;
import com.gitee.feizns.itrip.personal.biz.entity.vo.ForViewQueryOrderById;
import com.gitee.feizns.itrip.personal.biz.mapper.BizHotelOrderMapper;
import com.gitee.feizns.itrip.personal.biz.mapper.BizHotelRoomMapper;
import com.gitee.feizns.itrip.personal.biz.mapper.BizHotelTempStoreMapper;
import com.gitee.feizns.itrip.personal.biz.mapper.BizProductStoreMapper;
import com.gitee.feizns.itrip.personal.biz.service.HotelOrderService;
import com.gitee.feizns.itrip.personal.biz.service.exception.HotelOrderException;
import com.gitee.feizns.itrip.personal.mapper.HotelOrderMapper;
import com.gitee.feizns.itrip.personal.mapper.HotelTempStoreMapper;
import com.gitee.feizns.itrip.personal.mapper.ProductStoreMapper;
import com.gitee.feizns.itrip.personal.utils.DateUtils;
import com.gitee.feizns.itrip.personal.utils.Utils;
import lombok.NonNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author feizns
 * @since 2019/1/10 0010
 */
@Service
public class HotelOrderServiceImpl implements HotelOrderService {

    /**
     * 被忽略的订单类型
     */
    private static final int IGNORE_ORDER_TYPE = -1;

    /**
     * 被忽略的订单状态
     */
    private static final int IGNORE_ORDER_STATUS = -1;

    @Autowired
    private HotelOrderMapper hotelOrderMapper;

    @Autowired
    private BizHotelRoomMapper bizHotelRoomMapper;

    @Autowired
    private BizHotelOrderMapper bizHotelOrderMapper;

    @Autowired
    private HotelTempStoreMapper hotelTempStoreMapper;

    @Autowired
    private BizProductStoreMapper bizProductStoreMapper;

    @Autowired
    private BizHotelTempStoreMapper bizHotelTempStoreMapper;

    @Override
    public Page<ForViewPersonalOrderList> getPersonalOrderList(@NonNull ForAcceptGetPersonalOrderList condition, @NonNull Long curtUserId) {

        //纠正条件
        correctionForAcceptGetPersonalOrderList(condition);

        com.baomidou.mybatisplus.plugins.Page page =
                new com.baomidou.mybatisplus.plugins.Page(condition.getPageNo(), condition.getPageSize());

        page.setRecords(bizHotelOrderMapper.getPersonOrderList(page, condition, curtUserId));

        return new PageVoForMybatisPlus<>(page);
    }

    @Override
    public ForViewGetPersonalOrderRoomInfo getPersonalOrderRoomInfo(Long orderId) {
        return bizHotelOrderMapper.getPersonOrderRoomInfo(orderId);
    }

    @Override
    public ForViewGetPersonalOrderInfo getPersonalOrderInfo(Long orderId) {
        return bizHotelOrderMapper.getPersonalOrderInfo(orderId);
    }

    @Override
    public ForViewPreOrderInfo getPreOrderInfo(ForAcceptPreOrderInfo info) {
        ForViewPreOrderInfo forViewPreOrderInfo = bizHotelRoomMapper.queryHotelName(info.getRoomId());

        //检查库存
        int store = checkStore(info.getRoomId(), info.getCheckInDate(), info.getCheckOutDate(), info.getCount());

        forViewPreOrderInfo.setStore(store);
        BeanUtils.copyProperties(info, forViewPreOrderInfo, "roomId", "hotelId");
        return forViewPreOrderInfo;
    }

    @Override
    public ForViewQueryOrderById getOrderById(Long orderId) {
        return bizHotelOrderMapper.getOrderById(orderId);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public synchronized ForViewAddHotelOrder addHotelOrder(ForAcceptAddHotelOrder order, Long curtUserId) {

        //检查库存
        int store = checkStore(order.getRoomId(), order.getCheckInDate(), order.getCheckOutDate(), order.getCount());

        //补充内容
        HotelOrder newOrder = correctionForAcceptAddHotelOrder(order, curtUserId);

        //生成订单
        hotelOrderMapper.insert(newOrder);

        //添加临时存储
        insertAllHotelTempStore(newOrder, store);

        return new ForViewAddHotelOrder()
                .setId(newOrder.getId())
                .setOrderNo(newOrder.getOrderNo());
    }

    //5分钟判断一次
    @Scheduled(cron = "0 0/5 * * * ?")
    @Override
    public void flushOrder() {

        //设置为已取消
        bizHotelOrderMapper.flushOrderStatusToCancel(TWO_HOURS);

        //设置为已消费
        bizHotelOrderMapper.flushOrderStatusToConsumer();
    }

    @Override
    public int getStore(@NonNull Long roomId, @NonNull Date startDate, @NonNull Date endDate) {
        Integer store = bizHotelTempStoreMapper.checkTheInventoryOfTheRoomWithinTheSpecifiedTime(roomId, startDate, endDate);
        if ( store == null )
            store = bizProductStoreMapper.getStore(ProductStoreMapper.HOTEL_TYPE, roomId);

        Integer integer = bizHotelOrderMapper.queryPendingPaymentWithinTheSpecifiedTime(roomId, startDate, endDate);
        return store - (integer == null ? 0 : integer);
    }

    //检查库存
    private int checkStore(Long roomId, Date startDate, Date endDate, int count) {
        int store = getStore(roomId, startDate, endDate);
        if ( store < count )
            throw new HotelOrderException(HotelOrderException.INVENTORY_SHORTAGE, "库存不足!");
        return store;
    }

    //添加临时库存
    private void insertAllHotelTempStore(@NonNull HotelOrder order, int store) {
        Date temp = order.getCheckInDate();
        for (int i = 0; i < order.getBookingDays(); i++) {
            HotelTempStore tempStore = createTempStore(order, temp, store);
            hotelTempStoreMapper.insert(tempStore);
            temp = DateUtils.plusOneDay(temp);
        }
    }

    private HotelTempStore createTempStore(HotelOrder hotelOrder, Date date, int store) {
        return new HotelTempStore()
                .setHotelId(hotelOrder.getHotelId())
                .setRoomId(hotelOrder.getRoomId())
                .setRecordDate(date)
                .setStore(store)
                .setCreationDate(new Date());
    }

    private HotelOrder correctionForAcceptAddHotelOrder(ForAcceptAddHotelOrder order, Long curtUserId) {
        HotelOrder result = new HotelOrder();
        BeanUtils.copyProperties(order, result);

        return result.setUserId(curtUserId)
                .setCreatedBy(curtUserId)
                .setOrderNo(Utils.getMD5(Utils.getUUIDCode() + System.currentTimeMillis()))
                .setBookingDays(DateUtils.diffDay(order.getCheckInDate(), order.getCheckOutDate()))
                .setOrderStatus(HotelOrderMapper.ORDER_PENDING_PAYMENT_STATUS)
                .setLinkUserName(generatorLinkName(order.getLinkUser()))
                .setPayAmount(getPayAmount(order.getRoomId(), order.getCount(), result.getBookingDays()))
                .setCreationDate(new Date());
    }

    //计算金额
    private BigDecimal getPayAmount(Long roomId, int count, int days) {
        Double price = bizHotelRoomMapper.getPrice(roomId);
        return BigDecimal.valueOf(getAmount(price, count, days));
    }

    private Double getAmount(double price, int count, int days) {
        return price * count * days;
    }

    //生成联系人字符
    private static String generatorLinkName(List<ForAcceptAddHotelOrder.LinkUser> list) {
        if ( list != null )
            return list.stream()
                    .map(ForAcceptAddHotelOrder.LinkUser::getLinkUserName)
                    .collect(Collectors.joining(","));

        return null;
    }

    private void correctionForAcceptGetPersonalOrderList(@NonNull ForAcceptGetPersonalOrderList condition) {
        if ( condition.getOrderType() != null && condition.getOrderType() ==  IGNORE_ORDER_TYPE)
            condition.setOrderType(null);

        if ( condition.getOrderStatus() != null && condition.getOrderStatus() == IGNORE_ORDER_STATUS )
            condition.setOrderStatus(null);

        if ( condition.getPageNo() == null || condition.getPageNo() < 0 )
            condition.setPageNo(Page.MYBATIS_PLUS_DEFAULT_PAGE_NO);

        if ( condition.getPageSize() == null || condition.getPageSize() < 0 )
            condition.setPageSize(Page.DEFAULT_PAGE_SIZE);
    }

}
